/*
调度执行， 也就是决定当trigger动作触发effectFn执行的时候，有能力决定effectFn的执行的时机
*/

const bucket = new WeakMap()

let activeEffect
const effectStack = []
/**
 * 注册effect函数
 * @param {*} options 新增options对象，这里实现调度器函数的功能
 */
function effect(fn, options = {}) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(activeEffect)
    fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  }
  effectFn.options = options
  effectFn.deps = []
  effectFn()
}

function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  effectFn.deps.length = 0
}

function track(target, key) {
  if (!activeEffect) return
  let depsMap = bucket.get(target)
  if (!depsMap) bucket.set(target, (depsMap = new Map()))
  let deps = depsMap.get(key)
  if (!deps) depsMap.set(key, (deps = new Set()))
  deps.add(activeEffect)
  activeEffect.deps.push(deps)
}

function trigger(target, key) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const deps = depsMap.get(key)
  if (!deps) return
  const effectsToRun = new Set()
  deps.forEach((effectFn) => {
    if (effectFn !== activeEffect) {
      effectsToRun.add(effectFn)
    }
  })
  effectsToRun.forEach((effectFn) => {
    // 新增: 调度器使用
    if (effectFn.options.scheduler) {
      effectFn.options.scheduler(effectFn)
    } else {
      effectFn()
    }
  })
}

const data = { foo: 1, bar: 0 }
const obj = new Proxy(data, {
  get(target, key) {
    track(target, key)
    return Reflect.get(target, key)
  },
  set(target, key, val) {
    Reflect.set(target, key, val)
    trigger(target, key)
    return true
  },
})

effect(
  () => {
    console.log('scheduler changed:', obj.foo)
  },
  {
    // 调度器，接受一个fn，就是第一个参数传入的fn，用于指定调用时机
    scheduler(fn) {
      setTimeout(fn)
    },
  }
)
effect(() => console.log('changed:', obj.foo))

obj.foo++

console.log('结束了')

/*
新的需求，是如果多次调用obj.foo++， 比如我只关心最后的新状态，而不是中间状态(类似vue的数据多次变更，但是只会视图更新一次)
*/

// 任务队列
const jobQueue = new Set()
const p = Promise.resolve() // 异步任务，p.then以后再执行所有的jbQueue
// 刷新队列的标志, 在effect调度器中，可能会多次刷新任务队列，这里只让它刷新一次
let isFlushing = false
function flushJob() {
  if (isFlushing) return // 已经在调用flushJob了
  isFlushing = true
  p.then(() => {
    jobQueue.forEach((job) => job())
  }).finally(() => {
    isFlushing = false
  })
}

effect(
  () => {
    console.log('flushing bar', obj.bar)
  },
  {
    scheduler(fn) {
      jobQueue.add(fn)
      flushJob()
    },
  }
)

obj.bar++
obj.bar++ // 多次更新状态
